﻿using System;
using System.Data;
using LinqRepositorySample.Domain;
using NHibernate;
using Rhinestone.Shared;
using ITransaction=NHibernate.ITransaction;

namespace LinqRepositorySample.Data.NHibernate
{
    public class NHibernateUnitOfWork : IUnitOfWork
    {
        #region fields
        private readonly ISession _session;
        #endregion

        #region ctor
        /// <summary>
        /// Default Constructor.
        /// Creates a new instance of the <see cref="NHibernateUnitOfWork"/> class.
        /// </summary>
        /// <param name="session">The internal <see cref="ISession"/> that the
        /// NHibernateUnitOfWork wraps.</param>
        public NHibernateUnitOfWork(ISession session)
        {
            _session = session;
        }
        #endregion

        #region properties
        /// <summary>
        /// Gets whether the unit of work is currently operating in a transacted manner.
        /// </summary>
        /// <value>bool. True if the current <see cref="IUnitOfWork"/> instance is in
        /// a transaction.</value>
        public bool IsInTransaction
        {
            get { return _session.Transaction.IsActive; }
        }

        /// <summary>
        /// Gets the <see cref="ISession"/> that the current <see cref="NHibernateUnitOfWork"/> encapsulates.
        /// </summary>
        public ISession Session
        {
            get { return _session; }
        }
        #endregion

        #region methods
        /// <summary>
        /// Disposes of the <see cref="NHibernateUnitOfWork"/>.
        /// </summary>
        public void Dispose()
        {
            _session.Dispose();
        }

        /// <summary>
        /// Starts a new transaction for the <see cref="NHibernateUnitOfWork"/> instance.
        /// </summary>
        /// <returns>A <see cref="ITransaction"/> that wraps the internal NHibernate transaction.</returns>
        public Rhinestone.Shared.ITransaction BeginTransaction()
        {
            return BeginTransaction(IsolationLevel.ReadCommitted);
        }

        /// <summary>
        /// Starts a new transaction for the <see cref="NHibernateUnitOfWork"/> instance.
        /// </summary>
        /// <param name="isolationLevel">One of the values in <see cref="IsolationLevel"/> that specifies the isolation level
        /// of the transaction.</param>
        /// <returns></returns>
        public Rhinestone.Shared.ITransaction BeginTransaction(IsolationLevel isolationLevel)
        {
            Guard.Against<InvalidOperationException>(IsInTransaction,
                                                     "The unit of work instance is already running under a transaction.");
            return new NHibernateTransaction(_session.BeginTransaction(isolationLevel));
        }

        /// <summary>
        /// Flushes all changes made to the current unit of work to the data store.
        /// </summary>
        public void Flush()
        {
            _session.Flush();
        }

        /// <summary>
        /// Flushes all changes made to the current unit of work to the data store within
        /// a single transaction. By default <see cref="IsolationLevel.ReadCommitted"/> is used.
        /// </summary>
        public void TransactionalFlush()
        {
            TransactionalFlush(IsolationLevel.ReadCommitted);
        }

        /// <summary>
        /// Flushes all changes made to the current unit of work to the data store within
        /// a single transaction with the specified isolation level.
        /// </summary>
        /// <param name="isolationLevel">One of the values of <see cref="IsolationLevel"/> that specifies the isolation
        /// level of the transaction.</param>
        public void TransactionalFlush(IsolationLevel isolationLevel)
        {
            global::NHibernate.ITransaction transaction = _session.BeginTransaction(isolationLevel);
            try
            {
                _session.Flush();
                transaction.Commit();
            }
            catch { transaction.Rollback(); }
            finally { transaction.Dispose(); }
        }
        #endregion
    }
}
